﻿using JESAI.Core.Exceptions.Helpers;
using JESAI.Core.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace JESAI.Core.Extensions
{

    /// <summary>
    /// <see cref="IEnumerable{T}"/> 扩展类
    /// </summary>
    public static class EnumerableExtensions
    {
        #region ToConcurrentDictionary
        /// <summary>
        /// 将当前集合对象转成并发字典(属性名->属性值)
        /// </summary>
        public static ConcurrentDictionary<TKey, TValue> ToConcurrentDictionary<T, TKey, TValue>(this IEnumerable<T> collections, Func<T, TKey> keySelector, Func<T, TValue> valueSelector)
        {
            var dic = new ConcurrentDictionary<TKey, TValue>();
            foreach (var collect in collections)
            {
                var key = keySelector(collect);
                var value = valueSelector(collect);
                dic.TryAdd(key, value);
            }
            return dic;
        }
        #endregion

        /// <summary>
        /// 简化 Skip((pageIndex-1) * pageSize).Take(pageSize)调用,页码从1开始
        /// </summary>
        public static IEnumerable<T> GetPage<T>(this IEnumerable<T> sequence, int pageIndex, int pageSize) =>
            sequence.Skip((pageIndex - 1) * pageSize).Take(pageSize);

        /// <summary>
        /// 判断集合中是否存在元素
        /// </summary>
        public static bool EnumerableIsNotNullOrEmpty<T>(this IEnumerable<T> sequence) =>
            sequence != null && sequence.Any();

        /// <summary>
        /// 判断集合是否为空
        /// </summary>
        public static bool EnumerableIsNullOrEmpty<T>(this IEnumerable<T> sequence) =>
            sequence == null || !sequence.Any();

        /// <summary>
        /// 将当前集合中的元素根据指定的分隔符(<paramref name="separator"/>)拼接成字符串
        /// </summary>
        public static string ToStringSeparated<T>(this IEnumerable<T> sequence, string separator)
        {
            EnsureExceptionHelper.NotNull(sequence, nameof(sequence));

            if (!sequence.Any()) { return string.Empty; }
            return string.Join(separator, sequence);
        }

        /// <summary>
        /// 将当前集合中的元素根据指定的分隔符(<paramref name="delimiter"/>)拼接成字符串
        /// </summary>
        public static string ToCharSeparated<T>(this IEnumerable<T> sequence, char delimiter) =>
            ToStringSeparated(sequence, delimiter.ToString());

        /// <summary>
        /// 将当前集合中的元素根据分隔符(<c>,</c>)拼接成字符串
        /// </summary>
        public static string ToCommaSeparated<T>(this IEnumerable<T> sequence) =>
            ToCharSeparated(sequence, ',');

        /// <summary>
        /// 针对集合中的每个元素进行调用
        /// </summary>
        public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
        {
            EnsureExceptionHelper.NotNull(action, nameof(action));
            foreach (var item in sequence) { action(item); }
        }

        /// <summary>
        /// 从集合中随机选取一个元素
        /// </summary>
        public static T SelectRandom<T>(this IEnumerable<T> sequence)
        {
            var random = new Random(Guid.NewGuid().GetHashCode());
            return sequence.SelectRandom(random);
        }

        /// <summary>
        /// 根据指定的随机数生成器，从当前集合中随机选取一个元素
        /// </summary>
        public static T SelectRandom<T>(this IEnumerable<T> sequence, Random random)
        {
            if (sequence is ICollection<T> collection)
            {
                return collection.ElementAt(random.Next(collection.Count));
            }

            var count = 1;
            var selected = default(T);

            foreach (var element in sequence)
            {
                if (random.Next(count++) == 0)
                {
                    // Select the current element with 1/count probability
                    selected = element;
                }
            }
            return selected;
        }

        /// <summary>
        /// 从当前集合中随机选取所有的元素生成新的集合
        /// </summary>
        public static IEnumerable<T> Randomize<T>(this IEnumerable<T> sequence)
        {
            EnsureExceptionHelper.NotNull(sequence, nameof(sequence));
            return Randomize(sequence, new Random(Guid.NewGuid().GetHashCode()));
        }

        /// <summary>
        /// 根据指定的随机数生成器，从当前集合中随机选取所有的元素生成新的集合
        /// </summary>
        public static IEnumerable<T> Randomize<T>(this IEnumerable<T> sequence, Random random)
        {
            EnsureExceptionHelper.NotNull(sequence, nameof(sequence));
            EnsureExceptionHelper.NotNull(random, nameof(random));

            var buffer = sequence.ToArray();
            for (var i = 0; i < buffer.Length; i++)
            {
                var j = random.Next(i, buffer.Length);
                yield return buffer[j];

                buffer[j] = buffer[i];
            }
        }

        /// <summary>
        /// 将当前集合转为<seealso cref="IList{T}"/>
        /// </summary>
        public static IList<T> SpeculativeToList<T>(this IEnumerable<T> sequence) => sequence as IList<T> ?? sequence.ToList();

        /// <summary>
        /// 将当前集合转为<seealso cref="IReadOnlyList{T}"/>
        /// </summary>
        public static IReadOnlyList<T> SpeculativeToReadOnlyList<T>(this IEnumerable<T> sequence) => sequence as IReadOnlyList<T> ?? sequence.ToList();

        /// <summary>
        /// 将当前集合转为数组
        /// </summary>
        public static T[] SpeculativeToArray<T>(this IEnumerable<T> sequence) => sequence as T[] ?? sequence.ToArray();

        /// <summary>
        /// 将当前集合转为只读的<seealso cref="IEnumerable{T}"/>
        /// </summary>
        public static IEnumerable<T> ToReadOnlySequence<T>(this IEnumerable<T> sequence)
        {
            EnsureExceptionHelper.NotNull(sequence, nameof(sequence));
            return sequence is IReadOnlyList<T> ? sequence : sequence.Skip(0);
        }

        /// <summary>
        /// 将当前集合转为另一个允许遍历时处理异常的集合
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<T> HandleExceptionWhenYieldReturning<T>(
            this IEnumerable<T> sequence,
            Func<Exception, bool> exceptionPredicate,
            Action<Exception> actionToExecuteOnException)
        {
            EnsureExceptionHelper.NotNull(exceptionPredicate, nameof(exceptionPredicate));
            EnsureExceptionHelper.NotNull(actionToExecuteOnException, nameof(actionToExecuteOnException));

            var enumerator = sequence.GetEnumerator();

            while (true)
            {
                T result;
                try
                {
                    if (!enumerator.MoveNext()) { break; }
                    result = enumerator.Current;
                }
                catch (Exception e)
                {
                    if (exceptionPredicate(e))
                    {
                        actionToExecuteOnException(e);
                        yield break;
                    }
                    throw;
                }
                yield return result;
            }

            enumerator.Dispose();
        }
    }
}